const cvs = document.querySelector('canvas');
const ctx = cvs.getContext("2d");
const col = 6; // x
const row = 6; // y
const GWIDTH = 100;
const GHEIGHT = 100;
// const stone = '';
// const stone = '12 34 23 31 43'
// const stone = '20 11 12 02 22';// 3*3
// const stone ='02 06 34 31 27 46 55 54 12 23 61 75 72';
// const stone ='16 51 52 62 34 26 15 24 32';//7*7


let stone = [];
let hammer = 5;
let board = [];
let actorX = 0;
let actorY = 0;
let min_hammer = 999;
let min_path = [];
let secret = false;
let retry = 0;
/**
 * 
 * @param {Number} a 
 * @param {Number} b 
 * @returns Number smaller one
 */
function min(a, b){
    return(a < b) ? a : b;
}

// check if x is an integer or not
function integer(x) {
    let y = x - (x | 0);
    if(y == 0){
        return 1; 
    }
    else{
        return 0;
    }
}

function checkMove(gx, gy){
    if(gx < 0 || gy < 0){
        return false;
    }
    if(gx >= col || gy >= row){
        return false;
    }
    return true;
}

function putHammer(gx, gy){
    let x1 = pixelX(gx) + GWIDTH / 4;
    let y1 = pixelY(gy) + GHEIGHT / 6;
    let x2 = pixelX(gx) + 2*GWIDTH / 5;
    let y2 = pixelY(gy) + GHEIGHT / 2;
    ctx.fillStyle = 'black';
    ctx.fillRect(x1, y1, GWIDTH / 2, GHEIGHT / 3);
    ctx.fillRect(x2, y2, GWIDTH / 5, GHEIGHT / 3);
}

function pixelX(gx){
    return gx*GWIDTH;
}

function pixelY(gy){
    return gy*GHEIGHT;
}

function drawYellowBox(gx, gy){

    let x = pixelX(gx) + 4;
    let y = pixelY(gy) + 4;
    // console.log('yellow x,y = ' + x + ', ' + y);
    ctx.strokeStyle = 'yellow';
    ctx.strokeRect(x, y, GWIDTH-8, GHEIGHT-8);
}

function drawYellowRound(gx, gy){
    let cx = pixelX(gx) + GWIDTH / 2;
    let cy = pixelY(gy) + GHEIGHT / 2;
    radius = min(GWIDTH, GHEIGHT) / 2 - 4;
    ctx.strokeStyle = 'yellow';
    ctx.beginPath();
    ctx.arc(cx, cy, radius, 0, 2*Math.PI);
    ctx.closePath();
    ctx.stroke();
}

function drawStatus(msg){

    let x = GWIDTH;
    let y = (col + 2)*GHEIGHT;

    ctx.fillStyle = 'white';
    ctx.fillRect(x, y-GHEIGHT, GWIDTH*10, GHEIGHT);
    
    ctx.font = '46px courier';
    ctx.fillStyle = 'blue';
    ctx.fillText(msg, x, y - GHEIGHT / 2);

}

function draw(){
    ctx.fillStyle = 'red';
    ctx.fillRect(0, 0, col*GWIDTH, row*GHEIGHT);
    ctx.fillStyle = 'green';
    ctx.fillRect((col + 1)*GWIDTH, 0, GWIDTH, row*GHEIGHT);
    ctx.fillStyle = 'silver';
    ctx.fillRect(GWIDTH, (row+2)*GHEIGHT, 2*GWIDTH, GHEIGHT);
    ctx.fillStyle = 'black';
    ctx.font = '80px courier';
    ctx.fillText('重置', GWIDTH + 15, (row + 3)*GHEIGHT - 20);


    for(let r = 0; r < hammer; r++){
        putHammer(col + 1, r);
    }

    ctx.beginPath();
    ctx.strokeStyle = 'black';
    for (let r=0; r<=col; r++){
        ctx.moveTo(r*GWIDTH, 0);
        ctx.lineTo(r*GWIDTH, row*GHEIGHT);
    }
    for (let r=0; r<=row; r++){
        ctx.moveTo(0, r*GHEIGHT);
        ctx.lineTo(col*GWIDTH, r*GHEIGHT);
    }
    ctx.stroke();
    ctx.closePath();

    // base layer
    for (let y=0; y<row; y++){
        for (let x=0; x<col; x++){
            if(board[y][x] == 0){
                ctx.fillStyle = 'green';
                ctx.fillRect(x*GWIDTH+1,y*GHEIGHT+1,GWIDTH-2,GHEIGHT-2);
            }
            if(board[y][x] == 1){
                ctx.fillStyle = 'brown';
                cx = x*GWIDTH + GWIDTH/2;
                cy = y*GHEIGHT + GHEIGHT/2;
                radius = min(GHEIGHT,GWIDTH)/2 - 4;
                // console.log('cx, cy=' + cx + ', ' + cy + ' r=' + radius);
                ctx.beginPath();
                ctx.arc(cx, cy, radius, 0, 2*Math.PI, false);
                ctx.closePath();
                ctx.fill();
            }
            if(board[y][x] == 2){
                ctx.fillStyle = 'white';
                ctx.fillRect(x*GWIDTH+1, y*GHEIGHT+1, GWIDTH-2, GHEIGHT-2);
            }
         
        }
    }

    // drawActor after the board base is finished
    drawActor(actorX, actorY);
    if (secret) drawDir();
}

function drawActor(gx, gy){
    let cx = gx*GWIDTH + GWIDTH/2;
    let cy = gy*GHEIGHT + GHEIGHT/4;
    let radius = GHEIGHT / 6;
    ctx.fillStyle = 'blue';
    ctx.beginPath();
    ctx.arc(cx, cy, radius, 0, 2*Math.PI);
    ctx.closePath();
    ctx.fill();

    ctx.fillStyle = 'blue';
    ctx.fillRect(cx - radius, cy + radius, 2*radius, 2*radius);

    // board[gy][gx] = 3; // ??? draw is READ-ONLY, do NOT assign

    if (checkMove(gx, gy+1)==true){ 
        if(board[gy + 1][gx] == 0){
            drawYellowBox(gx, gy + 1);
        }
        if(board[gy + 1][gx] == 1){
            drawYellowRound(gx, gy + 1);
        }
    }
    if(checkMove(gx, gy-1)==true){
        if(board[gy - 1][gx] == 0){
            drawYellowBox(gx, gy - 1);
        }
        if(board[gy - 1][gx] == 1){
            drawYellowRound(gx, gy - 1);
        }
    }
    if(checkMove(gx+1, gy)==true){
        if(board[gy][gx + 1] == 0){
            drawYellowBox(gx + 1, gy);
        }
        if(board[gy][gx + 1] == 1){
            drawYellowRound(gx + 1, gy);
        }
    }
    if(checkMove(gx-1, gy)==true){
        if(board[gy][gx - 1] == 0){
            drawYellowBox(gx - 1, gy);
        }
        if(board[gy][gx - 1] == 1){
            drawYellowRound(gx - 1, gy);
        }
    }   

}

function genStone(){
    let m = Math.floor(col * row /4);

    stone = [];
    for (let r = 0; r < row; r ++){
        stone[r] = [];
        for(let c = 0; c < col; c ++){
            stone[r][c] = 0;
        }
    }

    for (let i = 0; i < m; i++){
        let x = Math.floor(Math.random()*col);
        let y = Math.floor(Math.random()*row);
        if((x + y) == 0 || stone[y][x] == 1){
            m++;
            continue;
        }

        stone[y][x] = 1;
    }

    let total = 0;

    for (let r = 0; r < row; r ++){
        for(let c = 0; c < col; c ++){
            total += stone[r][c];
        }
    }
    console.log('stone:', total);
    console.dir(stone);
    return stone;
}


function EAST(gx, gy){
    let x, y;
    x = (gx) * GWIDTH;
    y = (gy) * GHEIGHT;
    ctx.strokeStyle = 'black';
    ctx.moveTo(x + GWIDTH/5, y + GHEIGHT/2);
    ctx.lineTo(x + 4*GWIDTH/5, y + GHEIGHT/2);
    ctx.lineTo(x + 3*GWIDTH/5, y + 2*GHEIGHT/5);
    ctx.moveTo(x + 4*GWIDTH/5, y + GHEIGHT/2);
    ctx.lineTo(x + 3*GWIDTH/5, y + 3*GHEIGHT/5);
    ctx.stroke();
}

function WEST(gx, gy){
    let x, y;
    x = (gx) * GWIDTH;
    y = (gy) * GHEIGHT;
    ctx.strokeStyle = 'black';
    ctx.moveTo(x + 4*GWIDTH/5, y + GHEIGHT/2);
    ctx.lineTo(x + GWIDTH/5, y + GHEIGHT/2);
    ctx.lineTo(x + 2*GWIDTH/5, y + 2*GHEIGHT/5);
    ctx.moveTo(x + GWIDTH/5, y + GHEIGHT/2);
    ctx.lineTo(x + 2*GWIDTH/5, y + 3*GHEIGHT/5);
    ctx.stroke();
}

function NORTH(gx, gy){
    let x, y;
    x = (gx) * GWIDTH;
    y = (gy) * GHEIGHT;
    ctx.strokeStyle = 'black';
    ctx.moveTo(x + GWIDTH/2, y + 4*GHEIGHT/5);
    ctx.lineTo(x + GWIDTH/2, y + GHEIGHT/5);
    ctx.lineTo(x + 2*GWIDTH/5, y + 2*GHEIGHT/5);
    ctx.moveTo(x + GWIDTH/2, y + GHEIGHT/5);
    ctx.lineTo(x + 3*GWIDTH/5, y + 2*GHEIGHT/5);
    ctx.stroke();
}

function SOUTH(gx, gy){
    let x, y;
    x = (gx) * GWIDTH;
    y = (gy) * GHEIGHT;
    ctx.strokeStyle = 'black';
    ctx.moveTo(x + GWIDTH/2, y + GHEIGHT/5);
    ctx.lineTo(x + GWIDTH/2, y + 4*GHEIGHT/5);
    ctx.lineTo(x + 2*GWIDTH/5, y + 3*GHEIGHT/5);
    ctx.moveTo(x + GWIDTH/2, y + 4*GHEIGHT/5);
    ctx.lineTo(x + 3*GWIDTH/5, y + 3*GHEIGHT/5);
    ctx.stroke();
}

function drawStar(gx, gy){
    let x, y;
    x = (gx) * GWIDTH;
    y = (gy) * GHEIGHT;
    ctx.beginPath();
    ctx.strokeStyle = 'yellow';
    ctx.fillStyle = 'yellow';
    ctx.moveTo(x + GWIDTH/2, y + GHEIGHT/5);
    ctx.lineTo(x + GWIDTH/4, y + 4*GHEIGHT/5);
    ctx.lineTo(x + 5*GWIDTH/6, y + 2*GHEIGHT/5);
    ctx.lineTo(x + GWIDTH/6, y + 2*GHEIGHT/5);
    ctx.lineTo(x + 3*GWIDTH/4, y + 4*GHEIGHT/5);
    ctx.lineTo(x + GWIDTH/2, y + GHEIGHT/5);
    ctx.stroke();
    ctx.fill();
    ctx.closePath();
}

function initBoard(){
    actorX = 0;
    actorY = 0;

    while (true) {
        board = [];

        for (let y=0; y<row; y++){
            board[y] = [];
            for (let x=0; x<col; x++){
                board[y][x] = 0;
            }
        }

        if(retry == 0){
            stone = genStone();
        }

        parseStone(stone);
        findPath();
        if (min_hammer <= 5) {
            hammer = min_hammer + 1;
            break;
        }
    }
}

// 1. initMouseEvent() - copy
// 2. onClick() - only console.log(...)
// 3. use it.
function gridX(x){
    let gx = x / GWIDTH;
    gx = gx | 0; // remove decimal point first
    if (gx >= 0 && gx < col){
        return gx;  // early exit
    } 
    return -1; // exception case
}

function gridY(y){
    let gy = (y / GHEIGHT) | 0;
    if (gy >= 0 && gy < row) {
        return gy;
    }
    return -1; 
}

function checkNext(){
    for(let y = 0; y < row; y++){
        for(let x = 0; x < col; x++){
            let diff = Math.abs(actorX - x) + Math.abs(actorY - y);
            if(diff == 1){
                if(board[y][x] == 0 || (board[y][x] == 1 && hammer > 0)){
                    return true;
                }
            }
        }
    }
    return false;
}

/**
 * 
 * @returns 0=playing, 1=win ,2=lose.
 */
function checkWin(){
    let land = 0;
    for(let y = 0; y < row; y++){
        for(let x = 0; x < col; x++){
            if(board[y][x] == 0){
                land++;
                break;
            }
        }
    }
    if(land == 0){
        drawStatus('Win');
        return 1;
    }
    if(checkNext() == true){
        drawStatus('playing');
        return 0;
    }
    drawStatus('lose');
    return 2;
}

function dir2dx(dir){
    return (dir - 3) * (1 ^ (dir & 1));
}

function dir2dy(dir){
    return (dir - 2) * (dir & 1);
}

function valid(x, y) {
    if (x < 0 || y < 0) {
        return false;
    }
    if (x >= col || y >= row) {
        return false;
    }
    return true;
}

function onKey(event){
    let dir = 0;
    let gx, gy;

    if (event.key ==' ') {
        secret = !secret;
        draw();
        return;
    }

    if (event.key == 'w'){
        dir = 1;
    }
    if (event.key == 's'){
        dir = 3;
    }
    if (event.key == 'a'){
        dir = 2;
    }
    if (event.key == 'd'){
        dir = 4;
    }

    if(dir <= 0){
        return;
    }

    gx = actorX + dir2dx(dir);
    gy = actorY + dir2dy(dir);

    if(valid(gx, gy) == false){
        return;
    }

    if(board[gy][gx] == 0){
        board[actorY][actorX] = 2;
        actorX = gx;
        actorY = gy;
        board[actorY][actorX] = 2; // walked
    }
    if(board[gy][gx] == 1 && hammer > 0){
        board[actorY][actorX] = 2;
        actorX = gx;
        actorY = gy;
        board[actorY][actorX] = 2;
        hammer--;
    }

    checkWin();
    draw();
   
}

function onClick(event){
    let x = event.offsetX;
    let y = event.offsetY;
    let gx = gridX(x);
    let gy = gridY(y);
    let tx = (x / GWIDTH) | 0;
    let ty = (y / GHEIGHT) | 0;
    console.log('onClick x, y, gx, gy, tx, ty=', x, y, gx, gy, tx, ty);
    if (gx >= 0 && gy >= 0){
        let l = board[gy][gx];
        if (true == event.altKey) {
            l++;
            if (l > 2){
                l = 0;
            }
            board[gy][gx] = l;
            console.log('board gx, gy, l = ' + gx + ', ' + gy + ', ' + board[gy][gx]);
        }
        let diff = Math.abs(actorX - gx) + Math.abs(actorY - gy);
        if(diff == 1){
            if(board[gy][gx] == 0){
                board[actorY][actorX] = 2;
                actorX = gx;
                actorY = gy;
                board[actorY][actorX] = 2; // walked
            }
            if(board[gy][gx] == 1 && hammer > 0){
                board[actorY][actorX] = 2;
                actorX = gx;
                actorY = gy;
                board[actorY][actorX] = 2;
                hammer--;
            }
        }
        checkWin();
        draw();
        return;
    }
    if(tx>=1 && tx<=2 && ty==(row+2)){
        retry = 1;
        initBoard();
        draw();
    }
    return;
}

function initMouseEvent(){
    cvs.addEventListener("click", onClick);
    window.addEventListener("keydown", onKey, false);
}

// 0. const stone = '21 22'; // 6x6 or 8x8
// 1. parseStone(st) <- st = string
// 2. inside initBoard(), at last, run parseStone(stone);

function parseStone(st){
    for(let r = 0; r < row; r++){
        for(let c = 0; c < col; c++){
            board[r][c] = st[r][c];
        }
    }
    
}

function checkMoveBB(bb, gx, gy, h){
    if(gx < 0 || gy < 0){
        return false;
    }
    if(gx >= col || gy >= row){
        return false;
    }
    let l = bb[gy][gx];
    if(l == 0){
        return true;
    }
    if(l == 1 && h > 0){
        return true;
    }
    return false;
}

function dfsOne(bb, tx, ty, path, h, l){
    let old_land;

    if(checkMoveBB(bb, tx, ty, 1)){
        old_land = bb[ty][tx];
        bb[ty][tx] = 2; //walked
        path[path.length] = [tx, ty];
        if(old_land==0){
            dfs(bb, path, h, l-1);
        }
        else{
            dfs(bb, path, h+1, l);
        }
        bb[ty][tx] = old_land;
        path.length = path.length - 1;
    }
}

function dfs(bb, path, h, l){
    if(h >= min_hammer){
        return ;
    }

    if(l == 0){
        if(h < min_hammer){
            min_hammer = h;
            min_path = structuredClone(path);
        }
        return ;
    }

    let gx, gy;
    let pos;
    pos = path[path.length - 1];
    gx = pos[0];
    gy = pos[1];
    let tx, ty;
    //EAST
    tx = gx + 1;
    ty = gy;
    dfsOne(bb, tx, ty, path, h, l);
    //WEST
    tx = gx - 1;
    ty = gy;
    dfsOne(bb, tx, ty, path, h, l);
    //NORTH
    tx = gx;
    ty = gy - 1;
    dfsOne(bb, tx, ty, path, h, l);
    //SOUTH
    tx = gx;
    ty = gy + 1;
    dfsOne(bb, tx, ty, path, h, l);
}

function findPath(){
    min_hammer = 999;
    min_path = [];
    let land = 0;
    let path = [];
    let bb;
    
    bb = structuredClone(board);
    bb[0][0] = 2;  //walked
    for(let y=0; y<row; y++){
        for(let x=0; x<col; x++){
            if(bb[y][x] == 0){
                land++;
            }
        }
    }
    console.log('total_land=' + land);
    path[0] = [0, 0];
    dfs(bb, path, 0, land);
    console.log('min_hammer:', min_hammer);
}

function drawDir(){
    let x, y, x1, y1, x2, y2, x3, y3;
    for(i = 0; i < (min_path.length-1); i++){
        [x1, y1] = min_path[i];
        [x2, y2] = min_path[i+1];
        x = x2 - x1;
        y = y2 - y1;
        if(x == 1 && y == 0){
            EAST(x1, y1);
        }
        if(x == -1 && y == 0){
            WEST(x1, y1);
        }
        if(x == 0 && y == 1){
            SOUTH(x1, y1);
        }
        if(x == 0 && y == -1){
            NORTH(x1, y1);
        }
    }
    if(min_path.length > 0){
        [x3, y3] = min_path[min_path.length - 1];
        drawStar(x3, y3);
    }
}

initBoard();
draw();
initMouseEvent(); // 调用